home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-03-19 | 34.1 KB | 1,117 lines |
-
- #import "mCD_Controller.h"
- #import "PrefsController.h"
- #import "CD_DBase.subproj/CD_DBase.h"
-
- void fixCatNum(struct rsc_media_catnum_reply *);
-
- @implementation mCD_Controller
-
- /* implement a periodic update */
- void Trigger(DPSTimedEntry teNum, double new, id target)
- {
- [target EventLoop];
- }
-
- - EventLoopInit
- {
- teNum = DPSAddTimedEntry(updatePeriod, (DPSTimedEntryProc)Trigger,
- self, NX_BASETHRESHOLD);
- return self;
- }
-
- - EventLoop
- {
- int devReady;
- struct timeval timed_cd_Tval;
-
- if ( (cd_fd != 0) && !do_timed_updates ) {
- devReady = do_testunitready(cd_fd, &timed_cd_Tval, &tur_Ereply);
- if ( !devReady ) {
- /* Hmm, will this do what I want? */
- [self loadCD:self];
- }
- }
-
- if ( do_timed_updates ) [self updateCdStatus:self];
- return self;
- }
-
- - appDidInit:sender
- {
- if ( 0 != geteuid() ) {
- id nonRootErrorPanel;
- nonRootErrorPanel = [NXApp loadNibSection:"NonRoot.nib" owner:NXApp];
- [NXApp runModalFor:nonRootErrorPanel];
- exit(0);
- }
-
- [globPrefs initGlobalPreferences:self mainPanel:mainPanel ];
-
- /* NOTE: in NS-3.2, it seems that setAltIncrementValue will work
- * backwards, at least for vertical sliders. I still want
- * to have it though, it's better backwards than not at all
- */
- [leftVolumeSliderID setAltIncrementValue: 1.0];
- [rightVolumeSliderID setAltIncrementValue: 1.0];
- [pauseButtonID setShowsStateBy:NX_CONTENTS | NX_CHANGEBACKGROUND];
-
- cd_fd = 0;
- [unloadButtonID setEnabled:NO];
-
- [curTrackID setStringValue: "--"];
- [endTrackID setStringValue: "--"];
- [trackPlayTimeID setStringValue: "-:--:--"];
- [trackRemTimeID setStringValue: "-:--:--"];
- [discPlayTimeID setStringValue: "-:--:--"];
- [discRemTimeID setStringValue: "-:--:--"];
- [trackInfoID selectAll:self];
- [trackInfoID replaceSel: ""];
- [mainPanel makeKeyAndOrderFront:self];
-
- /* start up the periodic update of the cd status */
- do_timed_updates = NO; /* initial testing */
- updatePeriod = 1.0; /* every 1 second */
- updatePeriod = 0.5; /* crazy times for testing purposes */
- updatePeriod = 0.99; /* a little less crazy... */
- [self EventLoopInit];
-
- /* if the preferences are indeed set for a CD-ROM drive, then
- * try to load a music CD, and if it's already in there go
- * to display it. Note that this will not (yet) automatically
- * prompt to load in a CD if there isn't one there.
- */
- if ( [self openCdFd:FROM_CD_APP_DID_INIT] ) {
- [self displayCdInfo];
- }
- return self;
- }
-
- - appWillTerminate:sender
- {
- /* the purpose of this method is to handle "Quit" processing */
- BOOL wantUnload, abortQuit;
- int devReady, alertResult;
-
- if ( cd_fd == 0 ) return self; /* no device open, nothing to do */
-
- /* assume an unload of the CD drive will be wanted. If the device
- * is ready (ie, there's a disc in it), and if the disc is playing,
- * then prompt the user to find out of it's really wanted. If the
- * drive has no CD, or the CD isn't playing, then always do an
- * unload to prevent later confusion.
- */
-
- wantUnload = YES; /* assume an unload will be wanted */
- abortQuit = NO; /* and the Quit won't be aborted */
- devReady = do_testunitready(cd_fd, &cd_Tval, &tur_Ereply);
- if ( !devReady ) {
- /* there is a disc in the drive */
- if (cd_curpos.rsc_audio_status == RSC_ASTAT_PLAYING ) {
- alertResult = NXRunAlertPanel("mCD Quitting",
- "There is a music CD still playing. Should it be unloaded during quit?",
- "Yes", "No", "Cancel Quit", rawDevName);
- switch ( alertResult ) {
- case NX_ALERTALTERNATE:
- wantUnload = NO;
- break;
- case NX_ALERTOTHER:
- wantUnload = NO;
- abortQuit = YES;
- break;
- }
- }
- }
-
- if (wantUnload) [self unloadCD:self];
- else {
- /* user wants the music CD left in the drive, playing, but
- * we should at least enable the media-removal button (on
- * the drive) to work again
- */
- do_preventremoval_1e(cd_fd, NO, &cd_Ereply); /* ie, allow removal */
- }
-
- if (abortQuit) return NULL;
-
- return self; /* anything non-null, so app will terminate */
- }
-
- - (int)appPowerOffIn:(int)ms andSave:(int)aFlag
- {
- /* the purpose of this method is to handle logout processing,
- * which is not handled by the appWillTerminate method. I
- * don't really care about power-off processing, but this
- * seems to be the only way to handle logout processing
- */
-
- /* this method is described *very* briefly in the pre-3.0
- * concepts documentation. While it works in NS-3.x (on
- * motorola hardware, at least), I suspect NeXT is phasing
- * it out. If this routine was doing any significant amount
- * of work, it might need to send the Workspace manager a:
- - (int)extendPowerOffBy:(int)requestedMs actual:(int *)actualMs;
- * message to get more time to do the processing.
- */
-
- /* clean up after ourselves as quickly as possible. this is
- * basically a trimmed-down version of "unload" processing
- */
- int err;
-
- if ( cd_fd == 0 ) return 0; /* no device currently, ignore */
-
- do_preventremoval_1e(cd_fd, NO, &cd_Ereply); /* ie, allow removal */
- do_eject_1b(cd_fd, &cd_Tval, &cd_Ereply);
- err = ioctl(cd_fd, DKIOCEJECT, NULL);
- close( cd_fd );
- cd_fd = 0;
-
- return 0 ; /* not sure what this method needs to return... */
- }
-
- - updateCdStatus:sender
- {
- BOOL needUpdate, indexChanged;
- int tempHour, tempMin, tempSec;
- int lastCdIndex, lastAbsSecond;
- char buff[80];
-
- if ( cd_fd == 0 ) return self; /* no device yet, ignore */
-
- needUpdate = indexChanged = NO;
-
- do_readcurrentposition_42(cd_fd, &cd_curpos, &rcp_Ereply);
-
- switch (cd_curpos.rsc_audio_status) {
- case RSC_ASTAT_PAUSED:
- case RSC_ASTAT_PLAYING:
- break;
- case RSC_ASTAT_PLAYCOMPLETE:
- if ( [globPrefs consoleDebugMsgs] )
- printf("mCD debug: play complete\n");
- [self stopCD:self];
- break;
- case RSC_ASTAT_PLAYABORTED:
- if ( [globPrefs consoleDebugMsgs] )
- printf("mCD debug: play aborted\n");
- /* presumably not wanted: [self stopCD:self]; */
- break;
- case RSC_ASTAT_NONE:
- break;
- }
-
- lastCdIndex = curCdIndex;
- curCdIndex = (cd_curpos.track * 100) + cd_curpos.index;
- if ( lastCdIndex != curCdIndex ) needUpdate = indexChanged = YES;
-
- lastAbsSecond = curAbsSecond;
- curAbsSecond = (cd_curpos.abs_hour * 3600) + (cd_curpos.abs_min * 60) +
- cd_curpos.abs_sec;
- if ( lastAbsSecond != curAbsSecond ) needUpdate = YES;
-
- if ( needUpdate ) {
- [mainPanel disableFlushWindow];
-
- if ( cd_curpos.index < 2 ) {
- sprintf (buff, "%d", cd_curpos.track);
- }
- else {
- sprintf (buff, "%d.%d", cd_curpos.track, cd_curpos.index);
- }
- [curTrackID setStringValue: buff];
- if ( (cd_curpos.index == 0) && (cd_curpos.rel_sec > 0) ) {
- sprintf (buff, "- %02d:%02d",
- cd_curpos.rel_min, cd_curpos.rel_sec);
- }
- else {
- sprintf (buff, "%d:%02d:%02d", cd_curpos.rel_hour,
- cd_curpos.rel_min, cd_curpos.rel_sec);
- }
- [trackPlayTimeID setStringValue: buff];
- switch (cd_curpos.rsc_audio_status) {
- case RSC_ASTAT_PLAYING:
- /* the CD is playing right now... */
- if ( (indexChanged) &&
- (cd_curpos.index + cd_curpos.rel_sec > 0) &&
- [globPrefs consoleDebugMsgs] ) {
- printf("mCD debug: index %d.%d started %d:%02d:%02d-%02d into CD",
- cd_curpos.track, cd_curpos.index,
- cd_curpos.abs_hour, cd_curpos.abs_min,
- cd_curpos.abs_sec, cd_curpos.abs_frame);
- if ((cd_curpos.rel_hour > 0) ||
- (cd_curpos.rel_min > 0) || (cd_curpos.rel_sec > 0)) {
- if ( cd_curpos.index != 0 ) {
- printf(", %d:%02d:%02d into the song",
- cd_curpos.rel_hour, cd_curpos.rel_min,
- cd_curpos.rel_sec);
- }
- else {
- printf(", -%02d:%02d before the song",
- cd_curpos.rel_min, cd_curpos.rel_sec);
- }
- }
- printf("\n");
- }
- tempSec = toc.info[cd_curpos.track].elapsedSec - (
- (cd_curpos.rel_hour * 3600) +
- (cd_curpos.rel_min * 60) +
- cd_curpos.rel_sec );
- tempMin = tempSec / 60;
- tempSec = tempSec % 60;
- tempHour = tempMin / 60;
- tempMin = tempMin % 60;
- sprintf (buff, "%d:%02d:%02d", tempHour, tempMin, tempSec);
- [trackRemTimeID setStringValue: buff];
- tempSec = toc.info[100].elapsedSec - (
- (cd_curpos.abs_hour * 3600) +
- (cd_curpos.abs_min * 60) +
- cd_curpos.abs_sec );
- tempMin = tempSec / 60;
- tempSec = tempSec % 60;
- tempHour = tempMin / 60;
- tempMin = tempMin % 60;
- sprintf (buff, "%d:%02d:%02d", tempHour, tempMin, tempSec);
- [discRemTimeID setStringValue: buff];
- break;
- }
- sprintf (buff, "%d:%02d:%02d", cd_curpos.abs_hour,
- cd_curpos.abs_min, cd_curpos.abs_sec);
- [discPlayTimeID setStringValue: buff];
- [mainPanel reenableFlushWindow];
- [mainPanel flushWindowIfNeeded];
- }
-
- return self;
- }
-
- - ejectCD:sender
- {
- int devReady;
- char buff[80];
-
- if ( cd_fd == 0 ) return self; /* no device yet, ignore */
- devReady = do_testunitready(cd_fd, &cd_Tval, &tur_Ereply);
- if ( devReady ) return self; /* device is not ready, ignore */
-
- do_timed_updates = NO; /* initial testing */
-
- /* zap a few display fields to reduce confusion */
- [trackInfoID selectAll:self];
- [trackInfoID replaceSel: ""];
-
- [curTrackID setStringValue: "--"];
- [endTrackID setIntValue: toc.lasttrack];
- [trackPlayTimeID setStringValue: "-:--:--"];
- [trackRemTimeID setStringValue: "-:--:--"];
- [discPlayTimeID setStringValue: "-:--:--"];
- if( toc.info[100].min >= 60 ) {
- toc.info[100].hour++ ;
- toc.info[100].min -= 60;
- }
- sprintf (buff, "%u:%02u:%02u", toc.info[100].hour,
- toc.info[100].min, toc.info[100].sec);
- [discRemTimeID setStringValue: buff];
-
- do_preventremoval_1e(cd_fd, NO, &cd_Ereply); /* ie, allow removal */
- do_eject_1b(cd_fd, &cd_Tval, &cd_Ereply);
-
- return self;
- }
-
- - unloadCD:sender
- {
- int err;
-
- if ( cd_fd == 0 ) return self; /* no device yet, ignore */
- /* (note that we can do the unload processing even if there is
- * no disc in the drive, so we don't testunitready here ) */
-
- [self ejectCD:self];
-
- /* the difference between ejecting and unloading is the
- following commands: */
- err = ioctl(cd_fd, DKIOCEJECT, NULL);
- close( cd_fd );
- cd_fd = 0;
-
- [loadButtonID setEnabled:YES];
- [unloadButtonID setEnabled:NO];
- return self;
- }
-
- - loadCD:sender
- {
- if ( [self openCdFd:FROM_LOAD_CD_REQUEST] ) {
- [self displayCdInfo];
- }
-
- return self;
- }
-
- - displayCdInfo
- {
- int track;
- char buff[120];
- NXStream *songInfoStream;
-
- [self updateCdStatus:self];
- switch (cd_curpos.rsc_audio_status) {
- case RSC_ASTAT_PAUSED:
- case RSC_ASTAT_PLAYING:
- break;
- case RSC_ASTAT_PLAYCOMPLETE:
- case RSC_ASTAT_PLAYABORTED:
- case RSC_ASTAT_NONE:
- do_spinup_1b(cd_fd, &cd_Tval, &cd_Ereply);
- }
- do_readtoc_43(cd_fd, &toc, &cd_Ereply);
- [self fillTocTitles];
-
- [curTrackID setIntValue: 0];
- [endTrackID setIntValue: toc.lasttrack];
- switch (cd_curpos.rsc_audio_status) {
- case RSC_ASTAT_PAUSED:
- /* have to skip over this for now, due to the odd behavior
- * of the CD-ROM drive that NeXT used to sell, which
- * indicates it's paused if it's not actively playing and
- * if a play operation didn't just complete. This part
- * could be done on a drive-specific basis, once the code
- * is better organized.
- */
- /* do nothing but ensure pause button is right */
- /* [pauseButtonID setIntValue:1]; */
- break;
- case RSC_ASTAT_PLAYING:
- /* do nothing but ensure pause button is right */
- [pauseButtonID setIntValue:0];
- break;
- case RSC_ASTAT_PLAYCOMPLETE:
- case RSC_ASTAT_PLAYABORTED:
- case RSC_ASTAT_NONE:
- break;
- }
-
- [trackPlayTimeID setStringValue: "-:--:--"];
- [trackRemTimeID setStringValue: "-:--:--"];
- if( toc.info[100].min >= 60 ) {
- toc.info[100].hour++ ;
- toc.info[100].min -= 60;
- }
- sprintf (buff, "%u:%02u:%02u", toc.info[100].hour,
- toc.info[100].min, toc.info[100].sec);
- [discRemTimeID setStringValue: buff];
-
- if ( ! toc.discTitle )
- [cdTitleID setStringValue:"title of CD is not known"];
- else {
- tempTitle[0] = '\0';
- if ( toc.discPerformer ) {
- strcat(tempTitle, toc.discPerformer);
- strcat(tempTitle, " - ");
- }
- strcat(tempTitle, toc.discTitle);
- [cdTitleID setStringValue: tempTitle];
- }
-
- /* display the table of contents to the window, as rich-text */
- songInfoStream = NXOpenMemory(NULL, 0, NX_READWRITE);
- NXPrintf(songInfoStream,
- "{\\rtf0\\ansi{\\fonttbl\\f0\\fmodern Ohlfs;");
- NXPrintf(songInfoStream, "\\f1\\fnil Times-Roman;}\n");
- NXPrintf(songInfoStream, "\\pard\\tx460\\tx1060\\tx1260");
- NXPrintf(songInfoStream, "\\f0\\b0\\i0\\ulnone\\fs18");
- NXPrintf(songInfoStream, "\\fi-1260\\li1260\\fc0\\cf0\n");
- for ( track = toc.firsttrack; track <= toc.lasttrack; track++ ) {
- NXPrintf(songInfoStream,
- "\\f0\\fs18%3d)\t%02d:%02d\t-\t\\f1\\fs24 ",
- track,
- toc.info[track].elapsedSec / 60,
- toc.info[track].elapsedSec % 60);
- if ( toc.info[track].trackTitle )
- NXPrintf(songInfoStream, "%s", toc.info[track].trackTitle);
- else NXPrintf(songInfoStream, "???");
- if ( track != toc.lasttrack ) NXPrintf(songInfoStream, "\\\n");
- }
- NXPrintf(songInfoStream, "\n}\n");
- NXSeek(songInfoStream, 0, NX_FROMSTART);
- [trackInfoID selectAll:self];
- [trackInfoID replaceSelWithRichText: songInfoStream];
- NXCloseMemory(songInfoStream, NX_FREEBUFFER);
-
- return self;
- }
-
- - pauseCD:sender
- {
- /* note that this method is called from both the pause
- * button and a menu item. Due to the menu item, this
- * has to directly set the state of the button (instead
- * of just sending a message to "sender").
- */
- do_readcurrentposition_42(cd_fd, &cd_curpos, &rcp_Ereply);
- switch (cd_curpos.rsc_audio_status) {
- case RSC_ASTAT_PLAYING:
- /* it is playing, switch it to pause */
- do_pauseaudio_4b(cd_fd, 1, &cd_Ereply);
- [pauseButtonID setIntValue:1];
- break;
- case RSC_ASTAT_PAUSED:
- /* it is paused, switch it to playing */
- do_pauseaudio_4b(cd_fd, 0, &cd_Ereply);
- [pauseButtonID setIntValue:0];
- break;
- case RSC_ASTAT_PLAYCOMPLETE:
- case RSC_ASTAT_PLAYABORTED:
- case RSC_ASTAT_NONE:
- /* do nothing but ensure pause button is right */
- [pauseButtonID setIntValue:0];
- break;
- }
-
- return self;
- }
-
- - playCD:sender
- {
- int sTrack = 1, eTrack = toc.lasttrack;
-
- [self playTracks:sTrack to: eTrack];
-
- return self;
- }
-
- - playTrackRange: sender
- {
- /* target for the play-range button in the
- select track range panel */
- int sTrack, eTrack;
-
- sTrack = [newStartTrackID intValue];
- eTrack = [newEndTrackID intValue];
- [self playTracks:sTrack to: eTrack];
- [trackRangePanel performClose:self];
-
- return self;
- }
-
- - setLeftVolume:sender
- {
- [self setPlayVolumes:[sender intValue] :-1];
- return self;
- }
-
- - setRightVolume:sender
- {
- [self setPlayVolumes:-1 :[sender intValue]];
- return self;
- }
-
- - setPlayVolumes:(int) leftVol :(int) rightVol
- {
- int devReady;
-
- if ( cd_fd == 0 ) return self; /* no device yet, ignore */
- devReady = do_testunitready(cd_fd, &cd_Tval, &tur_Ereply);
- if ( devReady ) return self; /* device is not ready, ignore */
-
- if ( [globPrefs separateVolumes] || ((leftVol < 0) && (rightVol < 0))) {
- /* need to know current volume settings */
- do_modesense_pc_E(cd_fd, &cd_volumes, &cd_Ereply);
- if (leftVol < 0) leftVol = cd_volumes.ch0_vol;
- if (rightVol < 0) rightVol = cd_volumes.ch1_vol;
- }
- else {
- if (leftVol < 0) {
- cd_volumes.ch0_vol = leftVol = rightVol;
- [leftVolumeSliderID setIntValue:leftVol];
- }
- if (rightVol < 0) {
- cd_volumes.ch1_vol = rightVol = leftVol;
- [rightVolumeSliderID setIntValue:rightVol];
- }
- }
-
- do_modeselect_pc_E(cd_fd, leftVol, rightVol, &cd_Ereply);
- return self;
- }
-
- /* the following used by PrefController */
- - getVolumes:(int *)leftVolPtr :(int *)rightVolPtr
- {
- do_modesense_pc_E(cd_fd, &cd_volumes, &cd_Ereply);
- *leftVolPtr = cd_volumes.ch0_vol;
- *rightVolPtr = cd_volumes.ch1_vol;
- return self;
- }
-
- - showSelectTrackRange:sender
- {
- [newStartTrackID setIntValue: 1];
- [newEndTrackID setIntValue: toc.lasttrack];
- [trackRangePanel makeKeyAndOrderFront:self];
- [newStartTrackID selectText:self];
- return self;
- }
-
- - goNextTrack:sender
- {
- int sTrack, eTrack;
-
- if ( cd_curpos.track >= toc.lasttrack ) {
- return self;
- }
-
- sTrack = cd_curpos.track + 1;
- eTrack = toc.lasttrack; /* for now */
- [self playTracks:sTrack to: eTrack];
-
- return self;
- }
-
- - goPreviousTrack:sender
- {
- int sTrack, eTrack;
-
- /* goes to the beginning of the current track if we're more than
- a few seconds into it, otherwise the previous track */
- sTrack = cd_curpos.track;
- if ( (0 == cd_curpos.rel_hour) && (0 == cd_curpos.rel_min)
- && (5 > cd_curpos.rel_sec) ) {
- sTrack = cd_curpos.track - 1;
- }
-
- if ( sTrack < 1 ) sTrack = 1;
- eTrack = toc.lasttrack; /* for now */
- [self playTracks:sTrack to: eTrack];
-
- return self;
- }
-
- /* you might ask, why does this go thru all the trouble of
- * finding starting and ending times and doing a playaudio_msf
- * instead of just playing a track range?
- *
- * The answer is that I want flexibility such that the CD database
- * can *change* the start and end times for a track, for those
- * CD's which were manufactured with the wrong starting times.
- */
- - playTracks:(int)startTrack to: (int) endTrack
- {
- struct pa_msf tst_start = {0, 0, 0};
- struct pa_msf tst_end = {0, 0, 0};
- int aeTrack;
- int devReady;
-
- if ( cd_fd == 0 ) return self; /* no device currently, ignore */
- devReady = do_testunitready(cd_fd, &cd_Tval, &tur_Ereply);
- if ( devReady ) return self; /* device is not ready, ignore */
-
- /* calc the track after the end track*/
- if ( endTrack >= toc.lasttrack ) aeTrack = 100;
- else aeTrack = endTrack + 1;
-
- /* note that do_playaudio_msf_47 has no hours variable... */
- tst_start.min = (toc.info[startTrack].hour * 60)
- + toc.info[startTrack].min;
- tst_start.sec = toc.info[startTrack].sec;
- tst_start.frame = toc.info[startTrack].frame;
-
- tst_end.min = (toc.info[aeTrack].hour * 60) + toc.info[aeTrack].min;
- tst_end.sec = toc.info[aeTrack].sec;
- tst_end.frame = toc.info[aeTrack].frame;
- if ( tst_end.frame > 0 ) tst_end.frame--;
- else {
- tst_end.frame = 74;
- if ( tst_end.sec > 0 ) tst_end.sec--;
- else {
- tst_end.sec = 59;
- if ( tst_end.min > 0 ) tst_end.min--;
- }
- }
-
- if ( [globPrefs consoleDebugMsgs] ) {
- printf("mCD debug: start (%d) %02u:%02u-%02u",
- startTrack,
- tst_start.min, tst_start.sec, tst_start.frame);
- printf(" end (%d) %02u:%02u-%02u\n",
- endTrack,
- tst_end.min, tst_end.sec, tst_end.frame);
- }
- do_playaudio_msf_47(cd_fd, tst_start, tst_end, &cd_Ereply);
-
- /* make sure the volumes are set right (doing a playaudio command
- * causes the CD to play at full volume, even if modesense shows
- * that some other volume level has been set)
- */
- do_modesense_pc_E(cd_fd, &cd_volumes, &cd_Ereply);
- [leftVolumeSliderID setIntValue: cd_volumes.ch0_vol];
- [rightVolumeSliderID setIntValue: cd_volumes.ch1_vol];
- [self setPlayVolumes:cd_volumes.ch0_vol :cd_volumes.ch1_vol];
-
- /* ensure pause button is set right */
- [pauseButtonID setIntValue:0];
-
- return self;
- }
-
- - showInfoPanel:sender
- {
- if ( ! _mCD_InfoPanel )
- _mCD_InfoPanel = [NXApp loadNibSection:"mCD_Info.nib" owner:self];
- [_mCD_InfoPanel makeKeyAndOrderFront:self];
-
- return self;
- }
-
- - showTestCD:sender
- {
- if ( ! testCD_ID )
- [NXApp loadNibSection:"testCD.nib" owner:self];
- [testCD_ID showUsingPrefs:globPrefs];
-
- return self;
- }
-
- - stopCD:sender
- {
- int devReady;
- char buff[40];
-
- if ( cd_fd == 0 ) return self; /* no device yet, ignore */
- devReady = do_testunitready(cd_fd, &cd_Tval, &tur_Ereply);
- if ( devReady ) return self; /* device is not ready, ignore */
-
- do_rezerounit_01(cd_fd, &cd_Ereply);
- do_stopunit_1b(cd_fd, &cd_Tval, &cd_Ereply);
- curCdIndex = -1;
- curAbsSecond = -1;
-
- /* probably should reset time-displaying fields differently */
- [trackRemTimeID setStringValue: "-:--:--"];
- if( toc.info[100].min >= 60 ) {
- toc.info[100].hour++ ;
- toc.info[100].min -= 60;
- }
- sprintf (buff, "%u:%02u:%02u", toc.info[100].hour,
- toc.info[100].min, toc.info[100].sec);
- [discRemTimeID setStringValue: buff];
-
- return self;
- }
-
- - copyMcdEntry:sender
- {
- int track;
- NXStream *mstream;
- id pboard;
- NXAtom ptypes[1];
-
- /* note: probably should check that there really is an mCD entry
- * to copy at this point in time...
- */
- mstream = NXOpenMemory(NULL, 0, NX_READWRITE);
- if( toc.info[100].min >= 60 ) {
- toc.info[100].hour++ ;
- toc.info[100].min -= 60;
- }
- NXPrintf(mstream, "%u:%02u:%02u\t", toc.info[100].hour,
- toc.info[100].min, toc.info[100].sec);
-
- if ( ! toc.discTitle )
- NXPrintf(mstream, "**artist unknown**\t- ** title unknown **");
- else {
- if ( ! toc.discPerformer )
- NXPrintf(mstream, "**artist unknown**");
- else
- NXPrintf(mstream, "%s", toc.discPerformer);
- NXPrintf(mstream, "\t- %s", toc.discTitle);
- }
- if ( toc.discCatNum )
- NXPrintf(mstream, "\tUPC=%s", toc.discCatNum);
- NXPrintf(mstream, "\n");
-
- /* include the table of contents */
- for ( track = toc.firsttrack; track <= toc.lasttrack; track++ ) {
- NXPrintf(mstream, "%4d)\t%02d:%02d\t- ", track,
- toc.info[track].elapsedSec / 60,
- toc.info[track].elapsedSec % 60);
- if ( toc.info[track].trackTitle )
- NXPrintf(mstream, "%s", toc.info[track].trackTitle);
- else NXPrintf(mstream, "???");
- NXPrintf(mstream, "\n");
- }
- NXPrintf(mstream, "\n");
-
- pboard = [Pasteboard new];
- ptypes[0] = NXAsciiPboardType;
- [pboard declareTypes:ptypes num:1 owner:self];
- [pboard writeType:NXAsciiPboardType fromStream:mstream];
- NXCloseMemory(mstream, NX_FREEBUFFER);
-
- return self;
- }
-
- - copyMcdEntryAsObjC:sender
- {
- u_int key;
- int track;
- NXStream *mstream;
- id pboard;
- NXAtom ptypes[1];
-
- /* note: probably should check that there really is an mCD entry
- * to copy at this point in time...
- */
- mstream = NXOpenMemory(NULL, 0, NX_READWRITE);
- key = [cd_dbase indexKey:&toc];
- NXPrintf(mstream, "#define %s %10u /* key parts = %d.%02u.%02d.%d %d */\n",
- "0000_none_0000", key, toc.info[100].elapsedSec,
- toc.info[100].frame, toc.lasttrack, toc.info[1].elapsedSec,
- toc.info[toc.lasttrack].elapsedSec);
-
- if( toc.info[100].min >= 60 ) {
- toc.info[100].hour++ ;
- toc.info[100].min -= 60;
- }
- NXPrintf(mstream, " if ( cdKey == %s ) { /* %u:%02u:%02u-%02u */\n",
- "0000_none_0000", toc.info[100].hour,
- toc.info[100].min, toc.info[100].sec, toc.info[100].frame);
-
- if ( ! toc.discPerformer )
- NXPrintf(mstream, "\ttocPtr->discPerformer = \"performer??\";\n");
- else
- NXPrintf(mstream, "\ttocPtr->discPerformer = \"%s\";\n", toc.discPerformer);
-
- if ( ! toc.discTitle )
- NXPrintf(mstream, "\ttocPtr->discTitle = \"title??\";\n");
- else
- NXPrintf(mstream, "\ttocPtr->discTitle = \"%s\";\n", toc.discTitle);
-
- if ( toc.discCatNum )
- NXPrintf(mstream, "\ttocPtr->discCatNum = \"%s\";\n", toc.discCatNum);
-
- #ifdef ADD_SKIPSONGS
- NXPrintf(mstream, "# if !defined(SKIPSONGS_\n");
- #endif
-
- /* include the table of contents */
- for ( track = toc.firsttrack; track <= toc.lasttrack; track++ ) {
- NXPrintf(mstream, "\tSET_CDtt(%2d, %02d.%02d, \"", track,
- toc.info[track].elapsedSec / 60,
- toc.info[track].elapsedSec % 60);
- if ( toc.info[track].trackTitle )
- NXPrintf(mstream, "%s", toc.info[track].trackTitle);
- NXPrintf(mstream, "\");\n");
- }
-
- /* finish off the entry */
- #ifdef ADD_SKIPSONGS
- NXPrintf(mstream, "# endif\n");
- #endif
- NXPrintf(mstream, "\t}\n");
-
- pboard = [Pasteboard new];
- ptypes[0] = NXAsciiPboardType;
- [pboard declareTypes:ptypes num:1 owner:self];
- [pboard writeType:NXAsciiPboardType fromStream:mstream];
- NXCloseMemory(mstream, NX_FREEBUFFER);
-
- return self;
- }
-
- - pasteMcdEntry:sender
- {
- return self;
- }
-
- - (BOOL)openCdFd:(BOOL)tryToLoad
- {
- int devReady;
-
- strcpy(rawDevName, [globPrefs rawDeviceName]);
-
- if ( cd_fd > 0 ) close( cd_fd );
-
- if (tryToLoad == FROM_LOAD_CD_REQUEST) {
- cd_fd = open( rawDevName, O_RDONLY );
- if ( cd_fd < 0 ) {
- NXRunAlertPanel(0,
- "Error return from open() for device %s",
- 0, 0, 0, rawDevName);
- cd_fd = 0;
- return NO;
- }
- }
- else {
- cd_fd = open( rawDevName, O_RDONLY | O_NDELAY );
- if ( cd_fd < 0 ) {
- cd_fd = 0;
- return NO;
- }
- }
-
- /* should check the result of do_inquiry to see which model
- * of CD-ROM drive is there (so this would know which SCSI
- * commands can be sent to the drive) */
- do_inquiry(cd_fd, &cd_Inq, &cd_Ereply);
-
- if ( cd_Inq.ir_devicetype != DEVTYPE_CDROM ) {
- if (tryToLoad == FROM_LOAD_CD_REQUEST) {
- NXRunAlertPanel(0,
- "Device %s does not seem to be a CD-ROM drive",
- 0, 0, 0, rawDevName);
- }
- else {
- printf("Device %s does not seem to be a CD-ROM drive\n",
- rawDevName);
- }
- close( cd_fd );
- return NO;
- }
-
- devReady = do_testunitready(cd_fd, &cd_Tval, &tur_Ereply);
- if ( cd_Ereply.er_sensekey == SENSE_UNITATTENTION ) {
- /* drive is signalling attention because there is a new disc
- * in the drive since the last time it was checked. In this
- * context (loading in a CD), we don't care that it's new
- */
- devReady = do_testunitready(cd_fd, &cd_Tval, &tur_Ereply);
- }
- if ( (tryToLoad == FROM_LOAD_CD_REQUEST)
- && (devReady == -1)
- && (cd_Ereply.er_sensekey == SENSE_NOSENSE) ) {
- /* I don't understand what the culprit is that forces this
- * special case. It is needed on my NS/Intel box, but only
- * on the *first* time the "Load" button is selected after
- * the system is rebooted. For some reason, "loads" at that
- * point will not succeed unless an unload is first done on
- * the device...
- */
- int err;
- printf("mCD; device not ready, unloading and reloading the CD\n");
- err = ioctl(cd_fd, DKIOCEJECT, NULL);
- close( cd_fd );
- cd_fd = 0;
- cd_fd = open( rawDevName, O_RDONLY );
- if ( cd_fd < 0 ) {
- NXRunAlertPanel(0,
- "Error return from second open() on device %s",
- 0, 0, 0, rawDevName);
- cd_fd = 0;
- return NO;
- }
- devReady = do_testunitready(cd_fd, &cd_Tval, &tur_Ereply);
- }
- if ( devReady ) {
- if (tryToLoad == FROM_LOAD_CD_REQUEST) {
- printf("mCD: Device %s is not ready (%d, %d)\n", rawDevName,
- devReady, cd_Ereply.er_sensekey);
- /* the cd_fd is not freed here, on purpose */
- /* -------
- * Hmm. I think I'll change my mind on that. I forget
- * why I didn't want to free it, but now I think I do
- * at least for one case.
- */
- if ((devReady == -1) && (cd_Ereply.er_sensekey == SENSE_NOSENSE)) {
- int err;
- printf("mCD; device not ready, unloading the CD\n");
- err = ioctl(cd_fd, DKIOCEJECT, NULL);
- close( cd_fd );
- cd_fd = 0;
- }
- }
- else {
- /* freeing cd_fd in this case might be a preference option
- * to add someday... */
- close( cd_fd );
- }
- return NO;
- }
-
- if (tryToLoad == FROM_CD_APP_DID_INIT) {
- /* need to think more about under what circumstances the current
- * volumes should be set from global preference values
- */
- int leftInt, rightInt;
- [globPrefs getVolumes:&leftInt:&rightInt];
- cd_volumes.ch0_vol = leftInt;
- cd_volumes.ch1_vol = rightInt;
- [self setPlayVolumes:cd_volumes.ch0_vol :cd_volumes.ch1_vol];
- } else {
- do_modesense_pc_E(cd_fd, &cd_volumes, &cd_Ereply);
- }
- [leftVolumeSliderID setIntValue: cd_volumes.ch0_vol];
- [rightVolumeSliderID setIntValue: cd_volumes.ch1_vol];
-
- curCdIndex = -1;
- curAbsSecond = -1;
-
- /* do_preventremoval_1e(cd_fd, YES, &cd_Ereply); */
- do_timed_updates = YES; /* initial testing */
- [loadButtonID setEnabled:NO];
- [unloadButtonID setEnabled:YES];
- return YES; /* successfully setup */
- }
-
- - fillTocTitles
- {
- u_int key;
- struct rsc_media_catnum_reply cd_catnum;
- char origCatNumber[MEDIA_CATNUM_LENGTH];
- BOOL switchUPC;
-
- key = [cd_dbase indexKey:&toc];
-
- setreuid(-1, geteuid()); /* switch to real uid */
- [cd_dbase fillTocTitles:&toc givenKey:key];
- setreuid(-1, 0); /* back to being root */
-
- /* check out the media catalog number, if this CD has one */
- switch (cd_curpos.rsc_audio_status) {
- case RSC_ASTAT_PLAYING:
- /* don't read the media catalog number. Trying to read
- * it will stop the CD on the Apple CD300 and NeXT
- * drives. [on the Toshiba drive, it doesn't stop the
- * music, and in fact the command sometimes won't work
- * return the catalog number unless the CD *is* playing!]
- */
- if ( !toc.discCatNum && [globPrefs consoleDebugMsgs] ) {
- printf("mCD info: didn't check for media catalog number\n");
- }
- break;
- case RSC_ASTAT_PAUSED:
- /* it'd be nice to take the same precaution when the cd-rom
- * drive is paused, but it seems that the CD-ROM drive that
- * NeXT used to sell (a Sony mechanism) returns "paused" at
- * times when it's not really paused. The Apple CD300 (a
- * newer Sony mechanism) doesn't seem to have this problem.
- */
- case RSC_ASTAT_PLAYCOMPLETE:
- case RSC_ASTAT_PLAYABORTED:
- case RSC_ASTAT_NONE:
- switchUPC = NO;
- do_readmediacatnum_42(cd_fd, &cd_catnum, &cd_Ereply);
- if ( cd_catnum.catnum_isSet ) {
- memcpy(origCatNumber, cd_catnum.media_catnum, MEDIA_CATNUM_LENGTH);
- fixCatNum(&cd_catnum);
- if (!toc.discCatNum) switchUPC = YES;
- else {
- if ( strncmp(toc.discCatNum, cd_catnum.media_catnum, MEDIA_CATNUM_LENGTH)) {
- /* the UPC in our database does not match the UPC this
- * drive found on the CD itself. First have to check
- * for lame NeXT-CD-ROM case (which misses 3 chars)
- */
- if ( cd_catnum.media_catnum[MEDIA_CATNUM_LENGTH-1] != '?') switchUPC = YES;
- else {
- if ( strncmp(toc.discCatNum,cd_catnum.media_catnum,
- MEDIA_CATNUM_LENGTH-3)) switchUPC = YES;
- }
- }
- if ( switchUPC && [globPrefs consoleDebugMsgs]) {
- /* tell the user of mismatch */
- int i;
- u_char *cptr;
- printf("mCD info: original UPC = x'");
- cptr = &(origCatNumber[0]);
- printf("%.2X", *cptr++); /* ignore comp. warning */
- for (i=1; i<MEDIA_CATNUM_LENGTH; i++) {
- printf(" %.2X", *cptr++); /* ignore comp. warning */
- }
- printf("'\n");
- printf("mCD info: fixed up UPC = \"%.15s\"\n", cd_catnum.media_catnum);
- printf("mCD info: DBase return = \"%15s\"\n", toc.discCatNum);
- printf("mCD info: currently using the fixed upCD's catalog number\n");
- }
- }
- }
- if ( switchUPC ) {
- memcpy(holdCatNumber, cd_catnum.media_catnum, MEDIA_CATNUM_LENGTH);
- holdCatNumber[MEDIA_CATNUM_LENGTH+1] = '\0';
- toc.discCatNum = &(holdCatNumber[0]);
- }
- /* Redoing the "spinup" here seems to reduce the problem
- * of getting a unit attn when do_readmediacatnum finds
- * a media code on the music CD */
- do_spinup_1b(cd_fd, &cd_Tval, &cd_Ereply);
- break;
- }
-
- return self;
- }
-
-
- /* these next few are for the benefit of the testCD module,
- * to implement the protocol testCDstarter */
- - (int) fdOfCdrom { return cd_fd; }
- - (struct esense_reply*) esenseReplyPtr { return &cd_Ereply; }
- - (struct esense_reply*) esenseTestUnitPtr { return &tur_Ereply; }
- - (struct esense_reply*) esenseCurPosPtr { return &rcp_Ereply; }
-
- @end
-
- /* I've been testing this program on three different models of CD-ROM
- * drives. Each one returns the catalog number in a different format.
- * In the case of the CD-ROM drive NeXT used to sell, it doesn't even
- * return the whole number. Do the best we can to map the three
- * formats (that I know of so far) into a common format.
- *
- * Apple CD300 UPC = '3030 3735 3939 3235 3739 3432 3130 30'
- * DEC-Toshiba UPC = '0000 0705 0909 0205 0709 0402 0100 00'
- * NeXT CD-ROM UPC = '0075 9925 7942 0000 0000 0000 0000 00'
- *
- * Not sure if this routine should really be in the scsi_cd subproj...
- */
- void fixCatNum(catNumPtr)
- struct rsc_media_catnum_reply *catNumPtr;
- {
- char *tempPtr, *endPtr;
- BOOL all_char, all_dec;
-
- all_char = YES; /* all bytes are characters '0' to '9' */
- all_dec = YES; /* all bytes are 0x00 to 0x09 */
- tempPtr = &(catNumPtr->media_catnum[0]);
- endPtr = tempPtr + MEDIA_CATNUM_LENGTH;
- for (; tempPtr < endPtr; tempPtr++) {
- if (*tempPtr < '0' || *tempPtr > '9') all_char = NO;
- if (*tempPtr > 0x09) all_dec = NO;
- }
- if (all_dec) {
- /* all decimal, change them to all-characters */
- tempPtr = catNumPtr->media_catnum;
- for (; tempPtr < endPtr; tempPtr++) *tempPtr += 0x30;
- all_dec = NO;
- all_char = YES;
- }
- if (all_char) return;
-
- /* the NeXT format, have to expand it, going right-to-left */
- /* [done the brute-force way, and assumes MEDIA_CATNUM_LENGTH = 15] */
- catNumPtr->media_catnum[14] = (catNumPtr->media_catnum[7] >> 4) + 0x30;
- catNumPtr->media_catnum[13] = (catNumPtr->media_catnum[6] & 0x0F) + 0x30;
- catNumPtr->media_catnum[12] = (catNumPtr->media_catnum[6] >> 4) + 0x30;
- catNumPtr->media_catnum[11] = (catNumPtr->media_catnum[5] & 0x0F) + 0x30;
- catNumPtr->media_catnum[10] = (catNumPtr->media_catnum[5] >> 4) + 0x30;
- catNumPtr->media_catnum[9] = (catNumPtr->media_catnum[4] & 0x0F) + 0x30;
- catNumPtr->media_catnum[8] = (catNumPtr->media_catnum[4] >> 4) + 0x30;
- catNumPtr->media_catnum[7] = (catNumPtr->media_catnum[3] & 0x0F) + 0x30;
- catNumPtr->media_catnum[6] = (catNumPtr->media_catnum[3] >> 4) + 0x30;
- catNumPtr->media_catnum[5] = (catNumPtr->media_catnum[2] & 0x0F) + 0x30;
- catNumPtr->media_catnum[4] = (catNumPtr->media_catnum[2] >> 4) + 0x30;
- catNumPtr->media_catnum[3] = (catNumPtr->media_catnum[1] & 0x0F) + 0x30;
- catNumPtr->media_catnum[2] = (catNumPtr->media_catnum[1] >> 4) + 0x30;
- catNumPtr->media_catnum[1] = (catNumPtr->media_catnum[0] & 0x0F) + 0x30;
- catNumPtr->media_catnum[0] = (catNumPtr->media_catnum[0] >> 4) + 0x30;
- /* and after all that, we probably have three zeros at the end,
- * which most-likely means we've lost the last three digits.
- * Put in question-marks, I guess.
- */
- if ( (catNumPtr->media_catnum[14] == '0')
- && (catNumPtr->media_catnum[13] == '0')
- && (catNumPtr->media_catnum[12] == '0') ) {
- catNumPtr->media_catnum[12] = '?';
- catNumPtr->media_catnum[13] = '?';
- catNumPtr->media_catnum[14] = '?';
- }
- }
-